package com.wxd.framework.common.mybatis;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.support.SqlSessionDaoSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.util.Assert;

import com.wxd.framework.common.Criteria;
import com.wxd.framework.common.page.Pagination;
import com.wxd.framework.common.utils.GenericsUtils;

public class MyBatisEntityDao<T> extends SqlSessionDaoSupport {
	//---------------------自动
	public static final String	POSTFIX_SELECT_PRIAMARYKEY	= ".selectByPrimaryKey";
	public static final String	POSTFIX_DELETE_PRIAMARYKEY	= ".deleteByPrimaryKey";
	public static final String	POSTFIX_INSERT				= ".insert";
	public static final String	POSTFIX_INSERT_SELECTIVE	= ".insertSelective"; 
	public static final String	POSTFIX_UPDATE_SELECTIVE	= ".updateByPrimaryKeySelective";
	public static final String	POSTFIX_UPDATE	 		= ".updateByPrimaryKey";
	
	public static final String	POSTFIX_SELECT_PAGINATION_CRITERIA = ".selectPaginationByCriteria";
	
	public static final String	POSTFIX_SELECT_LIST_CRITERIA = ".selectListByCriteria";
	
	//---------------------手动
	public static final String	POSTFIX_SELECT_ALLLIST			= ".selectAllList";
	public static final String	POSTFIX_COUNT				= "_count";
	public static final String  POSTFIX_INSERT_BATCH		= ".insertInBatch";
	
	
	public static final String	POSTFIX_SELECT_LIST_MAP		= ".selectListByMap";	
	public static final String	POSTFIX_SELECT_LIST_SQL		= ".selectListBySql";
	public static final String	POSTFIX_DELETE				= ".delete";
	
	
	protected Class<T> entityClass;
	protected String primaryKeyName;
	protected String mapNamespace;
	
	/**
	 * 取得entityClass. JDK1.4不支持泛型的子类可以抛开Class<T> entityClass,重载此函数达到相同效果。
	 * 
	 * @return
	 */
	protected Class<T> getEntityClass() {
		return entityClass;
	}
	
	@SuppressWarnings("unchecked")
	public MyBatisEntityDao() {
		entityClass = GenericsUtils.getSuperClassGenricType(getClass());
		mapNamespace = this.getClass().getName();
	}
	
    @Autowired  
    public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory){  
        super.setSqlSessionFactory(sqlSessionFactory);  
    }

    //-----------------------------查询	
	/**
	 * 根据PrimaryKey获取对象
	 * @param id
	 * @return
	 */
	public T selectByPrimaryKey(Serializable id) {
		Object obj = getSqlSession().selectOne(mapNamespace + POSTFIX_SELECT_PRIAMARYKEY, id);
		if(obj != null){
			return (T)obj;
		}else {
			return null;
		}
	}
	
	/**
	 * 获取全部对象
	 * @return
	 */
	public List<T> selectAllList() {
		return getSqlSession().selectList(mapNamespace + POSTFIX_SELECT_ALLLIST);
	}
	
	/**
	 * 新增对象
	 */
	public int insert(Object obj){
		return getSqlSession().insert(mapNamespace + POSTFIX_INSERT, obj);
	}
	
	/**
	 * 新增对象（判断属性是否为空）
	 */
	public int insertSelective(Object obj){
		return getSqlSession().insert(mapNamespace + POSTFIX_INSERT_SELECTIVE, obj);
	}
	
	/**
	 * 批量插入
	 * @param list
	 * @return
	 */
	public <T> int insertInBatch(List<T> list){
		return getSqlSession().insert(mapNamespace + POSTFIX_INSERT_BATCH, list);
	}
	
	/**
	 * 保存对象  
	 */
	public int update(Object obj){
		return getSqlSession().update(mapNamespace + POSTFIX_UPDATE, obj);
	}
	
	/**
	 * 保存对象（判断属性是否为空）
	 */
	public int updateSelective(Object obj){
		return getSqlSession().update(mapNamespace + POSTFIX_UPDATE_SELECTIVE, obj);
	}
	
	/**
	 * 根据主键删除某个对象
	 * @param id
	 * @return
	 */
	public int deleteByPrimaryKey(Serializable id) {
		return getSqlSession().delete(mapNamespace + POSTFIX_DELETE_PRIAMARYKEY, id);
	}
	
	/**
	 * 根据Criteria 查询
	 * @param namespace
	 * @param criteria
	 * @return
	 */
	public <T> List<T> selectListByCriteria(Criteria criteria){
		return getSqlSession().selectList(mapNamespace + POSTFIX_SELECT_LIST_CRITERIA, criteria);
	}
		
	/**
	 * 根据Criteria 分页查询
	 * @param namespace
	 * @param criteria
	 * @return
	 */
	public Pagination getPaginationByCriteria(Criteria criteria){
		return pageQuery(mapNamespace + POSTFIX_SELECT_PAGINATION_CRITERIA, criteria);
	}
	
	public Pagination pageQuery(String sqlName, Criteria criteria){
		Integer pageNo = criteria.getPageNo();
		Integer pageSize = criteria.getPageSize();
		if(pageNo == null || pageSize == null){
			List list = getSqlSession().selectList(sqlName, criteria);
			if(list == null || list.size() == 0){
				Pagination p = new Pagination(0, 0, 0);
				p.setList(new ArrayList());
				return p;
			}else{
				return new Pagination(0, list.size(), list.size(), list);
			}
		}else{
			Assert.hasText(sqlName);
			Assert.isTrue(pageNo >= 1, "pageNo should start from 1");
			// Count查询
			Integer totalCount = (Integer) getSqlSession().selectOne(sqlName + POSTFIX_COUNT, criteria);
			if(totalCount < 1){
				Pagination p = new Pagination(0, 0, 0);
				p.setList(new ArrayList());
				return p;
			}
			Pagination pagination = new Pagination(pageNo, pageSize, totalCount);
			// 实际查询返回分页对象
			int startIndex = pagination.getFirstResultIndex();
			criteria.setStartIndex(startIndex);
			int size = pageSize;
			criteria.setEndSize(size);
			List list = getSqlSession().selectList(sqlName, criteria);
			pagination.setList(list);
			return pagination;
		}
	}
	
	protected <T> T selectOne(String statement, Object parameter){
		Object object = super.getSqlSession().selectOne(statement, parameter);
		return object != null ? (T)object : null;
	}
	
	
	//------------------------待完善
	/**
	 * 保存或更新
	 * TODO  待测试
	 * @param obj
	 */
	public void saveOrUpdate(Object obj) {
/*		if (updateSelective(obj) != 1) {
			insertSelective(obj);
		}*/
		Object primaryKey;
		try {
			primaryKey = getPrimaryKeyValue(obj);
		} catch (Exception e) {
			throw new ObjectRetrievalFailureException(entityClass, e);
		}
		if (primaryKey == null) {
			insert(obj);
		} else {
			update(obj);
		}
	}

	public void setPrimaryKeyName(String primaryKeyName) {
		this.primaryKeyName = primaryKeyName;
	}

	protected Object getPrimaryKeyValue(Object o)
			throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
		return PropertyUtils.getProperty(entityClass.newInstance(), getPrimaryKeyName());
	}

	public String getPrimaryKeyName() {
		if (StringUtils.isEmpty(primaryKeyName))
			primaryKeyName = "id";
		return primaryKeyName;
	}

}
